package dao

import (
	"context"
	"encoding/json"
	"time"

	"github.com/go-xorm/xorm"
	"github.com/olivere/elastic/v7"

	"im/common"
	"im/dao/es"
	"im/errcode"
	"im/model"
	"im/requests"
	"im/service/orm"
)

type PostsAdmin struct {
	db *xorm.Engine
}

func NewAdminPosts() PostsAdmin {
	return PostsAdmin{
		db: orm.GetDB(),
	}
}

type postsWithScore struct {
	EsPosts
	CalculatedScore float64 `json:"score"`
}

func (a PostsAdmin) List(param requests.Pagination) (ms []postsWithScore, count int64, err error) {
	e := es.New()
	client, err := e.GetClient()
	if err != nil {
		return
	}
	var (
		feedConfig = NewFeedConfig()
		r          postRules
	)
	scoreQuery := elastic.NewFunctionScoreQuery().
		AddScoreFunc(r.TimeAging(feedConfig.GetThresholds(RuleKeyAging), calcuScales(feedConfig.GetScale(RuleKeyAging)))). // 事件评分
		// 质量交互
		AddScoreFunc(r.QualityInteract(RuleKeyShares, feedConfig.GetChannelInteractThresholds(RuleKeyShares), feedConfig.GetChannelInteractScales(RuleKeyShares),
			feedConfig.GetThresholds(RuleKeyQualityInteractShares),
			calcuScales(feedConfig.GetScale(RuleKeyQuality), feedConfig.GetScale(RuleKeyQualityInteract)*feedConfig.GetScale(RuleKeyQualityInteractShares)))).
		AddScoreFunc(r.QualityInteract(RuleKeyLikes, feedConfig.GetChannelInteractThresholds(RuleKeyLikes), feedConfig.GetChannelInteractScales(RuleKeyLikes),
			feedConfig.GetThresholds(RuleKeyQualityInteractLikes),
			calcuScales(feedConfig.GetScale(RuleKeyQuality), feedConfig.GetScale(RuleKeyQualityInteract)*feedConfig.GetScale(RuleKeyQualityInteractShares)))).
		AddScoreFunc(r.QualityInteract(RuleKeyComments, feedConfig.GetChannelInteractThresholds(RuleKeyComments), feedConfig.GetChannelInteractScales(RuleKeyComments),
			feedConfig.GetThresholds(RuleKeyQualityInteractComments),
			calcuScales(feedConfig.GetScale(RuleKeyQuality), feedConfig.GetScale(RuleKeyQualityInteract)*feedConfig.GetScale(RuleKeyQualityInteractComments)))).
		AddScoreFunc(r.QualityInteract(RuleKeyPrivateMessage, feedConfig.GetChannelInteractThresholds(RuleKeyPrivateMessage), feedConfig.GetChannelInteractScales(RuleKeyPrivateMessage),
			feedConfig.GetThresholds(RuleKeyQualityInteractPrivateMessages),
			calcuScales(feedConfig.GetScale(RuleKeyQuality), feedConfig.GetScale(RuleKeyQualityInteract)*feedConfig.GetScale(RuleKeyQualityInteractPrivateMessages)))).
		// 质量.浏览
		AddScoreFunc(r.QualityViewOpen(feedConfig.GetThresholds(RuleKeyQualityViewOpen), // 质量.浏览.打开评分
			calcuScales(feedConfig.GetScale(RuleKeyQuality), feedConfig.GetScale(RuleKeyQualityView)*feedConfig.GetScale(RuleKeyQualityViewOpen)))).
		AddScoreFunc(r.QualityViewPartReads(feedConfig.GetThresholds(RuleKeyQualityViewPartReads), // 质量.浏览.有效浏览评分
			calcuScales(feedConfig.GetScale(RuleKeyQuality), feedConfig.GetScale(RuleKeyQualityView), feedConfig.GetScale(RuleKeyQualityViewPartReads)))).
		AddScoreFunc(r.QualityViewAllReads(feedConfig.GetThresholds(RuleKeyQualityViewAllReads), // 质量.浏览.读完评分
			calcuScales(feedConfig.GetScale(RuleKeyQuality), feedConfig.GetScale(RuleKeyQualityView), feedConfig.GetScale(RuleKeyQualityViewAllReads)))).BoostMode("sum").ScoreMode("sum")
	// 编码score函数
	result, err := client.Search().Index(es.WithPrefix(IndexNameOfPosts)).
		// todo
		Query(elastic.NewBoolQuery().Must(scoreQuery)).
		From(param.GetOffset()).Size(param.Limit).
		TrackScores(true).
		SortBy(elastic.NewFieldSort("id").Desc()).
		Do(context.Background())

	if err != nil {
		err = errcode.NewError(errcode.EsError, err)
		return
	}
	hits := result.Hits.Hits
	count = result.Hits.TotalHits.Value
	for _, hit := range hits {
		var em = EsPosts{}
		json.Unmarshal(hit.Source, &em)
		m := postsWithScore{}
		m.Posts = em.Posts
		m.CalculatedScore = *hit.Score
		m.RecommendMeasuresOfEs = em.RecommendMeasuresOfEs
		ms = append(ms, m)
	}
	return
}

type PostsPutByParam struct {
	model.RecommendMeasuresOfEs
	// 状态,1为可见, 2为隐藏(删除)
	SeeStatus int64 `json:"see_status"`
}

func (p PostsAdmin) Update(id int64, param PostsPutByParam) error {
	var err error
	if param.SeeStatus > 0 {
		_, err = p.db.Where("id=?", id).Update(&model.Posts{
			SeeStatus: param.SeeStatus,
		})
	} else {
		_, err = p.db.Where("id=?", id).Update(&model.Posts{
			RecommendMeasures: model.RecommendMeasures{
				Placeholder1: param.Placeholder1.String(),
				Placeholder2: param.Placeholder2.String(),
				Placeholder3: param.Placeholder3.String(),
				Placeholder4: param.Placeholder4.String(),
				Placeholder5: param.Placeholder5.String(),
				Placeholder6: param.Placeholder6.String(),
			},
			RecommendScore: param.RecommendMeasuresOfEs.WeightedScore(),
		})
	}
	if err != nil {
		err = errcode.NewError(errcode.DaoFailed, err)
		return err
	}
	m, err := p.GetById(id)
	if err != nil {
		return err
	}
	err = p.IndexDoc(m.Id, m, true)
	return err
}
func (p PostsAdmin) GetById(id int64) (model.Posts, error) {
	var m = model.Posts{}
	found, err := p.db.ID(id).Get(&m)
	if err != nil {
		return m, errcode.NewError(errcode.DaoFailed, err)
	}
	if !found {
		return m, errcode.NewError(errcode.RequestParamInvalid)
	}
	return m, nil
}
func (p PostsAdmin) GetByIds(ids ...int64) ([]model.Posts, error) {
	var ms = []model.Posts{}
	err := p.db.In("id", common.ToInterfaceSlice(ids)...).Find(&ms)
	if err != nil {
		return ms, errcode.NewError(errcode.DaoFailed, err)
	}
	return ms, nil
}
func (p PostsAdmin) IndexDoc(id int64, m model.Posts, wait bool) error {
	esM := EsPosts{
		Posts: m,
		TimeModelOfEs: model.TimeModelOfEs{
			time.Time(m.TimeModel.Created),
			time.Time(m.TimeModel.Updated),
		},
		RecommendMeasuresOfEs: model.RecommendMeasuresOfEs{
			Placeholder1: model.NewRecommendMeasure(m.Placeholder1),
			Placeholder2: model.NewRecommendMeasure(m.Placeholder2),
			Placeholder3: model.NewRecommendMeasure(m.Placeholder3),
			Placeholder4: model.NewRecommendMeasure(m.Placeholder4),
			Placeholder5: model.NewRecommendMeasure(m.Placeholder5),
			Placeholder6: model.NewRecommendMeasure(m.Placeholder6),
		},
	}
	err := es.New().IndexDocWait(IndexNameOfPosts, id, esM, wait)
	return err
}
